#include <iostream>
#include <memory>

// unique_ptr：独占指针
// 该指针只能控一块内存，这块内存只能为它所用

class Test
{
public:
    Test():a(std::make_unique<int>())
    {
        std::cout << "构造函数!\n";
    }

    ~Test()
    {
        std::cout << "析构函数!\n";
    }

    void show()
    {
        std::cout << "helloworld\n";
    }

private:
    std::unique_ptr<int>a;
};

int main()
{
#if 0
    // 智能指针的实现和使用

    // 智能指针能够自动释放对象的原因是：RAII（把堆上空间和一个对象的生命周期相绑定）
    // 光RAII还不够，还用模板做了一个通用版托管指针的类
    // 运算符重载使得指针能像普通指针一样用
    // RAII + 模板 + 运算符重载

    // unique_ptr是一个类的对象，它是用模板实现的，构造函数传给一个指针，这个指针来自堆上空间
    // std::unique_ptr<Test> p(new Test);
    // 上面这种写法没有任何问题
    // 但官方提供了一个模板函数，用来构建智能指针对象，推荐
    // 它会直接在堆上构建一个对象，然后返回一个智能指针
    std::unique_ptr<Test> p = std::make_unique<Test>();

    p->show(); // 可以像普通指针一样使用
    // (*p).show(); // 这样使用也可以
#endif

#if 0
    // std::unique_ptr<Test> p2 = p; 
    // std::unique_ptr<Test> p2(p); 
    // 上面两种写法没有任何区别，都会调用拷贝构造，都会报错
    // 独占：自己在堆上创建一块空间，由自己管理，然后这块空间既不能拷贝也不能赋值，也就意味着这块空间为我独占
    // 实现空间独占的原理：删除拷贝构造和赋值运算符重载
    
    // 裸指针：直接指向内存地址的指针变量。它们不具备自动管理内存的功能，也不负责指向内存的生命周期管理
    // 智能指针和裸指针不能混用，非常危险！！！
    // 一旦选择使用智能指针就不要再用裸指针
    // get()：获取裸指针的函数
    Test *t = p.get(); // 这样就可以拿到里面的裸指针进行使用，但非常危险，不要这么做
    t->show();
    delete t; // 当拿这个指针调用函数设置一些数据参数之后，可能会随手把它delete掉，可能会导致二次释放
#endif

#if 0
    // 独占指针可以预防浅拷贝
    Test t1;
    Test t2(t1); // 不允许拷贝
#endif
    return 0;
}